Skip to main content

第02章 事务

第2章 事务

一、事务传播行为

  • @Transaction(propagation = Propagation.REQUIRED):如果有事务就加入事务,没有的话就加入事务
  • @Transaction(propagation = Propagation.NOT_SUPPORT):容器不为这个方法开启事务
  • @Transaction(propagation = Propagation.REQUIRED_NEW):不管是否存在事务,都创建一个新的事物,原来的挂起,新的执行完毕之后,继续执行老的事务
  • @Transaction(propagation = Propagation.MANDATORY):必须在一个已有的事务里面执行,否则抛出异常
  • @Transaction(propagation = Propagation.NEVER):必须在一个没有的事务里面执行,否则抛出异常
  • @Transaction(propagation = Propagation.SUPPORT):如果其他的bean调用这个方法,在其他的bean里面声明了事务就使用事务,如果其他的bean里面没有声明事务,那就不用事务

二、事务隔离级别

  • @Transaction(isolation = Isolation.READ_UNCOMMITTED):读取未提交的数据(会出现脏读,不可重复读的)基本不使用
  • @Transaction(isolation = Isolation.READ_COMMITTED):读取已经提交的数据(会出现不可重复读还有幻读)SQLSERVER默认。因为读取已提交的数据,未提交的数据不能读,所以避免了脏读的问题。
  • @Transaction(isolation = Isolation.REPEATABLE_READ):可重复性读(会出现幻读)MYSQL默认。因为读走的数据加了锁,这样别的数据就不能改,避免了不可重复性读的问题。但是还是会出现幻读,因为可能会有别的事务添加几行新数据,这就导致可能两次相同的读取在后一次读取的时候发现多出来几行
  • @Transaction(isolation = Isolation.SERILIZABLE):串行化执行,最严格的级别。把整个表格锁死,幻读也可以避免了!

三、概念

  • 脏读: 事务 T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。这就是脏读1
  • 不可重复读:事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,发现得到了不同的结果。这就是不可重复读
  • 幻读:事务A读取值,把读取走的数据锁死了,但是事务B往里面添加了几行数据,这就导致事务A再次读取的时候发现可能多了几行符合条件的。就像幻影一样
  • 综上需要权衡,在性能和隔离程度直接选择。

四、锁

  • 读锁:读取锁防止其他事务在事务结束之前更改事务期间读取的数据,从而防止不可重复的读取。其他事务可以读取数据,但不能写入数据。当前事务也被禁止进行更改。
  • 写锁,写入锁用于更新。写锁防止其他事务在当前事务完成之前更改数据,但允许其他事务和当前事务本身进行脏读取。换句话说,事务可以读取自己未提交的更改。
  • 独占写锁:独占写入锁用于更新。防止其他事务读取或更改数据,直到当前事务完成。它还可以防止其他事务的脏读取。
  • 乐观离线锁:保持乐观的状态,默认不会出现冲突。用户写入数据的时候,检查一下读取的数据是否发生了改变,如果发现不一致,那就是说明用户是基于陈旧的数据在进行修改,就会抛出异常。(实现方法:增加一个版本号,更新之后版本号码加1,校验的时候检查版本号码是否有问题,当然也可以用时间戳)
  • 悲观离线锁:对于冲突这个事情保持悲观太多,也就是认为冲突的概率非常高。当有用户来获取Session的时候就会获取锁,一旦有人在读取或者修改,就无法获取锁。
  • 粗粒锁Coarse-Grained Lock :把根数据相关的,比如外键关联的所有的数据全部锁死。实现原理;两组数据通过引用共享同一个版本号码,通过引用的方法,两个数据引用的是同一个版本号码。

五、多个数据库更新

  • 两阶段提交协议:首先第一阶段是问,问一下数据库A,是否可以提交,再问一下B数据库是否可以提交,最后如果两个都符合,然后就可以提交了(第二阶段)。

六、binlog、redolog、undolog

  • redo是重做的意思,redo log就是重做日志的意思。保证数据不会丢失。在修改之后,先将修改后的值记录到磁盘上的redo log中,就算突然断电了,Buffer Pool中的数据全部丢失了,来电的时候也可以根据redo log恢复Buffer Pool,这样既利用到了Buffer Pool的内存高效性,也保证了数据不会丢失。
  • undo是撤销的意思,我们都知道,InnoDB是支持事务的,而事务是可以回滚的。假如一个事务将age=1修改成了age=2,在事务还没有提交的时候,后台线程已经将age=2刷入了磁盘。这个时候,不管是内存还是磁盘上,age都变成了2,如果事务要回滚,找不到修改之前的age=1,无法回滚了。那怎么办呢?很简单,把修改之前的age=1存起来,回滚的时候根据存起来的age=1回滚就行了。
  • binlog是归档数据,undo log记录的是修改之前的数据,提供回滚的能力。redo log记录的是修改之后的数据,提供了崩溃恢复的能力。binlog记录的是修改之后的数据,用于归档。里面记录一些SQL执行的语句。由于SQL的兼容性广,可能可以在别的数据库里面直接执行。